﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Reflection;
using System.Reflection.Emit;
using Phydeaux.Utilities;
using System.Windows.Controls.Primitives;
using System.Security;
using System.Windows.Media;
using System.Windows.Input;

namespace Bixas.Tangibilis.Utility
{
    public class PointUtilWrapper
    {
        private static Phydeaux.Utilities.Func<Point, Point, PresentationSource, bool, bool> _TryClientToRootWrapper;
        private delegate Point test(Point point, PresentationSource presentationSource, bool throwOnError,  out bool success);
  
        private static Phydeaux.Utilities.Func<Point, Point, PresentationSource> _ScreenToClientWrapper;
        private static Phydeaux.Utilities.Func<Point, Point, PresentationSource> _ClientToScreenWrapper;

        static MethodInfo tryClientToRootMethod;

        public static Point ScreenToClient(Point point, PresentationSource presentationSource)
        {
            return _ScreenToClientWrapper(point, presentationSource);
        }

        public static Point ClientToScreen(Point point, PresentationSource presentationSource)
        {
            return _ClientToScreenWrapper(point, presentationSource);
        }
        private static test deltest;
        static PointUtilWrapper()
        {
            InitWrappers();
            Type pointUtilType = typeof(InputDevice).Assembly.GetType("MS.Internal.PointUtil");
            tryClientToRootMethod = pointUtilType.GetMethod("TryClientToRoot", BindingFlags.Static | BindingFlags.Public);
		
        }

       [SecurityCritical, SecurityTreatAsSafe]
        public static Point TryClientToRoot(Point point, PresentationSource presentationSource, bool throwOnError, out bool success)
        {
            success = default(bool);
            object[] parameters = new object[] { point, presentationSource, throwOnError, success };
            Point result = (Point)tryClientToRootMethod.Invoke(null, parameters);
            success = (bool)parameters[3];
            return result;
        }

        private static void InitWrappers()
        {
            #region Init ExecuteCommandSource method wrapper

            //retrieve internal type MS.Internal.Commands.CommandHelpers
            Type commandHelpersType = System.Reflection.Assembly.GetAssembly(typeof(UIElement))
                                                            .GetType("MS.Internal.PointUtil");
            //retrieve internal procedure ExecuteCommandSource(ICommandSource commandSource)
            MethodInfo TryClientToRootMethod = commandHelpersType.GetMethod("TryClientToRoot", System.Reflection.BindingFlags.Public | BindingFlags.Static);
            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm = new DynamicMethod(TryClientToRootMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(Point),
                                                 new Type[] { typeof(Point), typeof(PresentationSource), typeof(bool), typeof(bool).MakeByRefType() },
                                                 commandHelpersType,
                                                 true);
            dm.InitLocals = false;
            dm.DefineParameter(3, ParameterAttributes.Out, "success");

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de = new DynamicEmit(dm);
            de.LoadArgument(0);
            de.LoadArgument(1);
            de.LoadArgument(2);
            de.LoadArgumentAddress(3);
            de.Call(TryClientToRootMethod);
            //TODO: verify if cast is needed when return type is void
            de.CastTo(TryClientToRootMethod.ReturnType, typeof(Point));
            de.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            deltest = (test)dm.CreateDelegate(typeof(test));
            //_TryClientToRootWrapper = (Phydeaux.Utilities.Func<Point, Point, PresentationSource, bool, bool>)dm.CreateDelegate(typeof(Phydeaux.Utilities.Func<Point, Point, PresentationSource, bool, bool>));

            #endregion

            MethodInfo ScreenToClientMethod = commandHelpersType.GetMethod("ScreenToClient", System.Reflection.BindingFlags.NonPublic | BindingFlags.Static);
            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm2 = new DynamicMethod(ScreenToClientMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(Point),
                                                 new Type[] { typeof(Point), typeof(PresentationSource) },
                                                 commandHelpersType,
                                                 true);
            dm2.InitLocals = false;

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de2 = new DynamicEmit(dm2);
            de2.LoadArgument(0);
            de2.LoadArgument(1);
            de2.Call(ScreenToClientMethod);
            //TODO: verify if cast is needed when return type is void
            de2.CastTo(ScreenToClientMethod.ReturnType, typeof(Point));
            de2.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            _ScreenToClientWrapper = (Phydeaux.Utilities.Func<Point, Point, PresentationSource>)dm2.CreateDelegate(typeof(Phydeaux.Utilities.Func<Point, Point, PresentationSource>));

            MethodInfo ClientToScreenMethod = commandHelpersType.GetMethod("ClientToScreen", new Type[] { typeof(Point), typeof(PresentationSource) });
            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm3 = new DynamicMethod(ClientToScreenMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(Point),
                                                 new Type[] { typeof(Point), typeof(PresentationSource) },
                                                 commandHelpersType,
                                                 true);
            dm3.InitLocals = false;

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de3= new DynamicEmit(dm3);
            de3.LoadArgument(0);
            de3.LoadArgument(1);
            de3.Call(ClientToScreenMethod);
            //TODO: verify if cast is needed when return type is void
            de3.CastTo(ClientToScreenMethod.ReturnType, typeof(Point));
            de3.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            _ClientToScreenWrapper = (Phydeaux.Utilities.Func<Point, Point, PresentationSource>)dm3.CreateDelegate(typeof(Phydeaux.Utilities.Func<Point, Point, PresentationSource>));

        }



        internal static Matrix GetVisualTransform(Visual v)
        {
            if (v == null)
            {
                return Matrix.Identity;
            }
            Matrix identity = Matrix.Identity;
            Transform transform = VisualTreeHelper.GetTransform(v);
            if (transform != null)
            {
                Matrix matrix2 = transform.Value;
                identity = Matrix.Multiply(identity, matrix2);
            }
            Vector offset = VisualTreeHelper.GetOffset(v);
            identity.Translate(offset.X, offset.Y);
            return identity;
        }


        [SecurityCritical, SecurityTreatAsSafe]
        public static Point TryClientToRootZ(Point point, PresentationSource presentationSource, bool throwOnError, out bool success)
        {
            if (throwOnError || (((presentationSource != null) && (presentationSource.CompositionTarget != null))))
            {
                point = presentationSource.CompositionTarget.TransformFromDevice.Transform(point);
                point = TryApplyVisualTransform(point, presentationSource.RootVisual, true, throwOnError, out success);
                return point;
            }
            success = false;
            return new Point(0.0, 0.0);
        }

        public static Point TryApplyVisualTransform(Point point, Visual v, bool inverse, bool throwOnError, out bool success)
        {
            success = true;
            if (v != null)
            {
                Matrix visualTransform = GetVisualTransform(v);
                if (inverse)
                {
                    if (!throwOnError && !visualTransform.HasInverse)
                    {
                        success = false;
                        return new Point(0.0, 0.0);
                    }
                    visualTransform.Invert();
                }
                point = visualTransform.Transform(point);
            }
            return point;
        }

 


 

    }
}
